Fundamentals
Primitives Vs Objects
Primitive Types
Primitive values are immutable.
const name = "John";
name.toUpperCase(); // returns "JOHN"
console.log(name); // still "John"
Primitive types:
stringnumberbigintbooleansymbolnullundefined
Boxed Types
JavaScript provides object wrappers for primitive types.
Examples:
String
Number
Boolean
Avoid creating boxed values directly:
const name = new String("John"); // avoid
Instead use primitive values:
const name = "John";
JavaScript automatically creates temporary wrapper objects when needed:
"hello".toUpperCase();
// is converted to
String("hello").toUpperCase();
Internally, JavaScript temporarily treats the string like a String object to access methods.
Null Vs Undefined
Undefined
Assigned automatically by JavaScript when a value has not been provided.
let user;
console.log(user); // undefined
Null
Explicitly assigned by developers to indicate the intentional absence of a value.
let user = null;
Recommendation: Use
nullwhen you want to represent "no value". Avoid manually assigningundefined.
Equality
Strict Equality (Recommended)
Always prefer ===.
1 === 1; // true
"1" === 1; // false
It compares both type and value.
Loose Equality
Avoid == because it performs type coercion.
"1" == 1; // true
false == 0; // true
Common Exception
Checking for both null and undefined:
if (value == null) {
// value is null or undefined
}
Equivalent to:
if (value === null || value === undefined) {
}
Objects
Everything that is not a primitive is an object.
Examples:
- Objects
- Arrays
- Functions
- Classes
- Dates
- Maps
- Sets
- Regular expressions
Objects are mutable.
const user = { name: "John" };
user.name = "Jane";
Object References
Objects are compared by reference, not by value.
const a = { name: "John" };
const b = { name: "John" };
console.log(a === b); // false
Even though the contents are identical, they occupy different locations in memory.
const c = a;
console.log(a === c); // true
Comparing by Value
For simple objects:
JSON.stringify(a) === JSON.stringify(b);
This works only for simple cases and is not recommended for large or complex objects.
Object.freeze()
Object.freeze() prevents modifications to an object.
const user = {
name: "John",
};
Object.freeze(user);
user.name = "Jane"; // ignored
However, freezing is shallow:
const user = {
profile: {
age: 30,
},
};
Object.freeze(user);
user.profile.age = 31; // still works
Nested objects remain mutable unless they are also frozen.
Copying Objects
Shallow Copy
Copies only the top level.
const original = {
name: "John",
address: {
city: "London",
},
};
const copy = { ...original };
copy.address.city = "Paris";
console.log(original.address.city); // Paris
Nested objects still share the same reference.
Deep Copy
Creates completely independent copies.
const copy = structuredClone(original);
Now changes to nested values do not affect the original object.
Type Checking
Typeof
Useful for primitive values.
typeof "hello"; // "string"
typeof 42; // "number"
typeof true; // "boolean"
typeof Symbol(); // "symbol"
typeof undefined; // "undefined"
Also works for functions:
typeof("hello"); // "string"
typeof(() => {}); // "function"
Typeof Null
typeof null; // "object"
This is a historical JavaScript bug kept for compatibility.
To check for null:
value === null;
Arrays
Array.isArray(value);
Avoid:
typeof value === "object";
because arrays are objects.
Integers
Number.isInteger(value);
NaN
To check for NaN:
Number.isNaN(value);
- Do not use typeof to check NaN
typeof NaN; // "number"
Instanceof
Checks whether an object was created from a constructor.
[] instanceof Array; // true
{} instanceof Object; // true
new Date() instanceof Date; // true
/abc/ instanceof RegExp; // true
Most Precise Type Check
Object.prototype.toString.call(value);
Examples:
Object.prototype.toString.call([]);
// "[object Array]"
Object.prototype.toString.call(new Date());
// "[object Date]"
Object.prototype.toString.call(null);
// "[object Null]"
- Create a helper to get exact type
function getType(value) {
return Object.prototype.toString
.call(value)
.slice(8, -1)
}
console.log(getType([])) // "Array"
console.log(getType({})) // "Object"
console.log(getType(null)) // "Null"
console.log(getType(undefined)) // "Undefined"
console.log(getType(new Date())) // "Date"
console.log(getType(/abc/)) // "RegExp"
console.log(getType(new Map())) // "Map"
console.log(getType(() => {})) // "Function"
Why Do We Need the Stack and Heap?
JavaScript stores data in memory using two main areas:
Stack
The stack is fast and stores small, fixed-size values.
Typical examples:
- Function call information
- Primitive values (
string,number,boolean, etc.) - References (memory addresses) to objects
const age = 30;
const name = "John";
Heap
- The heap stores larger, dynamic data structures.
- Heap memory means: A region of memory used for dynamic allocation
- Typical examples:
- Objects
- Arrays
- Functions
- Class instances
- Dates, Maps, Sets, etc.
const user = {
name: "John",
age: 30,
};
The object itself lives in the heap, while the variable user stores a reference to it.
Stack
user: 0x8F42
│
▼
Heap
[Free Memory box]
0x8F42 -> {
name: "John",
age: 30
}
[Free Memory box]
Difference Between var, let, and const
- There are 3 types of scope:
- Global scope
- Variables declared outside any function or block.
- Accessible throughout the program (unless shadowed by another variable).
- Function scope
- Variables declared inside a function.
- Accessible only within that function.
- Block scope (lexical scope)
- Variables declared with
letorconstinside a block (). - Accessible only within that block.
- Variables declared with
- Global scope
let And const
- Block-scoped (
{}) - Accessible only within the block where they are declared
const
- Block-scoped
- Must be initialized when declared.
- Cannot be reassigned after initialization
var
- Function-scoped
function example() {
if (true) {
var a = 1;
let b = 2;
const c = 3;
}
console.log(a); // 1
console.log(b); // ReferenceError
console.log(c); // ReferenceError
}
example();
const user = { name: "John" };
// ❌ Cannot reassign
// user = { name: "Jane" };
// ✅ But object contents can still be modified
user.name = "Jane";
Recommendation: Use
constby default. Useletwhen the variable needs to be reassigned. Avoidvarin modern JavaScript.
Logical Operators
&& and || do not necessarily return booleans.
They return one of their operands.
OR (||)
Returns the first truthy value.
const value1 = "hello" || ""; // "hello"
const value2 = false || true; // true
AND (&&)
Returns the first falsy value or the last truthy value.
"hello" && ""; // ""
true && false; // false
"hello" && "world"; // "world"
true && true; // true
Falsy Values
JavaScript has eight falsy values:
false
0
-0
0n
""
null
undefined
NaN
Everything else is truthy.
if("hello" && ""){
//won't reach here because "" is falsy
}
Type Conversion
Number Conversion
Number("123"); // 123
parseInt("123px"); // 123
parseFloat("12.5px"); // 12.5
Unary Plus
A short way to convert to a number:
const age = +"42";
String Conversion
String(123); // "123"
Boolean Conversion
Boolean(1); // true
Boolean(0); // false